package org.dragonnova.lamp.message;

import org.dragonnova.lamp.ProtocolDefined;
import org.dragonnova.lamp.utils.ByteUtil;
import org.dragonnova.lamp.utils.DataUtil;
import org.dragonnova.lamp.utils.HexUtil;

import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;

/**
 * * 指令格式：
 * ****************************** *
 * **   HEADER 2 bytes         ** *
 * **   REMOTE 1 bytes         ** *
 * **   LOCAL  1 bytes         ** *
 * **   LENGTH 2 bytes         ** *
 * **   ACTION 2 bytes         ** *
 * **   DEVICE 1 bytes         ** *
 * **   DATA   ? bytes         ** *
 * **   CS     2 bytes         ** *
 * ****************************** *
 */

/**
 * Created by hucn on 2016/8/17.
 * Description:
 */
public class Protocol implements IProtocol {

    private static final String TAG = Protocol.class.getSimpleName();

    public IMessage decode(byte[] data) {
        if (null == data || data.length == 0) {
            throw new IllegalArgumentException();
        }

        IMessage msg = new LampMessage();

        ByteBuffer msgBuffer = ByteBuffer.wrap(data);

        // 获取头消息
        byte[] header = new byte[ProtocolDefined.LENGTH_HEADER];
        try {
            msgBuffer.get(header, 0, header.length);
        } catch (BufferUnderflowException e) {
            msg.setAction(ProtocolDefined.ERROR_HEADER);
            return msg;
        }
        if (header[0] != ProtocolDefined.HEADER[0] || header[1] != ProtocolDefined.HEADER[1]) {
            msg.setAction(ProtocolDefined.ERROR_HEADER);
            return msg;
        }

        // 目标ID地址与通讯类型
        byte[] remote = new byte[ProtocolDefined.LENGTH_REMOTE];
        try {
            msgBuffer.get(remote, 0, remote.length);
        } catch (BufferUnderflowException e) {
            msg.setAction(ProtocolDefined.ERROR_REMOTE);
            return msg;
        }
        byte remoteId = ByteUtil.getHigh4Bit(remote[0]);
        byte remoteCommunicationType = ByteUtil.getLow4Bit(remote[0]);

        msg.setRemoteId(remoteId);
        msg.setRemoteCommunicationType(remoteCommunicationType);

        // 源ID地址与通讯类型
        byte[] local = new byte[ProtocolDefined.LENGTH_LOCAL];
        try {
            msgBuffer.get(local, 0, local.length);
        } catch (BufferUnderflowException e) {
            msg.setAction(ProtocolDefined.ERROR_LOCAL);
            return msg;
        }
        byte localId = ByteUtil.getHigh4Bit(local[0]);
        byte localCommunicationType = ByteUtil.getLow4Bit(local[0]);

        msg.setLocalId(localId);
        msg.setLocalCommunicationType(localCommunicationType);

        // 获取有效长度
        byte[] length = new byte[ProtocolDefined.LENGTH_DATA_SIZE];
        try {
            msgBuffer.get(length, 0, length.length);
        } catch (BufferUnderflowException e) {
            msg.setAction(ProtocolDefined.ERROR_LENGTH);
            return msg;
        }

        int dataLength = DataUtil.bytesToIntLittleEndian(length);
        if (dataLength < 2 || dataLength > data.length - 8) {
            msg.setAction(ProtocolDefined.ERROR_LENGTH);
            return msg;
        }

        // 获取有效的数据
        byte[] content = new byte[dataLength];
        try {
            msgBuffer.get(content, 0, content.length);
        } catch (BufferUnderflowException e) {
            msg.setAction(ProtocolDefined.ERROR_DATA);
            return msg;
        }

        byte action = content[0];
        byte deviceType = content[1];
        byte[] validData = new byte[content.length - 2];
        System.arraycopy(content, 2, validData, 0, validData.length);

        msg.setAction(action);
        msg.setDeviceType(deviceType);
        msg.setData(validData);

        // 获取校验位
        byte[] checkSum = new byte[ProtocolDefined.LENGTH_CHECKSUM];
        try {
            msgBuffer.get(checkSum, 0, checkSum.length);
        } catch (BufferUnderflowException e) {
            msg.setAction(ProtocolDefined.ERROR_CHECKSUM);
            return msg;
        }

        boolean isCorrect = checkSum(content, checkSum);
        if (!isCorrect) {
            msg.setAction(ProtocolDefined.ERROR_CHECKSUM);
            return msg;
        }
        return msg;
    }

    private boolean checkSum(byte[] content, byte[] checkSum) {
        int sum = 0;
        for (int i = 0; i < content.length; i++) {
            sum += content[i];
        }
        byte[] result = DataUtil.int2byteLen2Low(sum);
        return (result[0] + checkSum[0]) == (byte)0xFF && (result[1] + checkSum[1]) == (byte)0xFF;
    }

    private byte[] checkSum(byte action, byte deviceType, byte[] data) {
        int sum = 0;
        sum += action;
        sum += deviceType;
        if (data != null) {
            for (byte b : data) {
                sum += b;
            }
        }
        byte[] checksum = DataUtil.int2byteLen2Low(sum);
        return new byte[]{(byte) ~(checksum[0]), (byte) ~(checksum[1])};
    }

    public byte[] encode(IMessage message) {
        if (message == null) {
            throw new IllegalArgumentException("The message is null!");
        }

        // 添加头
        ByteBuffer bb = ByteBuffer.allocate(getTotalLen(message));
        bb.put(ProtocolDefined.HEADER, 0, ProtocolDefined.LENGTH_HEADER);

        // 添加id和通信类型
        byte remote = getRemote(message.getRemoteId(), message.getRemoteCommunicationType());
        byte local = getLocal(message.getLocalId(), message.getLocalCommunicationType());
        bb.put(new byte[]{remote}, 0, ProtocolDefined.LENGTH_REMOTE);
        bb.put(new byte[]{local}, 0, ProtocolDefined.LENGTH_LOCAL);

        // 添加数据长度
        int validLength = getDataLen(message.getData()) + ProtocolDefined.LENGTH_ACTION + ProtocolDefined.LENGTH_DEVICE;
        bb.put(DataUtil.int2byteLen2Low(validLength), 0, ProtocolDefined.LENGTH_DATA_SIZE);

        // 添加命令类型
        byte action = message.getAction();
        bb.put(new byte[]{action}, 0, ProtocolDefined.LENGTH_ACTION);

        // 添加设备类型
        byte device = message.getDeviceType();
        bb.put(new byte[]{device}, 0, ProtocolDefined.LENGTH_DEVICE);

        // 添加数据
        byte[] data = message.getData();
        if (data != null) {
            bb.put(data, 0, data.length);
        }

        // 添加校验位
        byte checksum[] = checkSum(action, device, data);
        bb.put(checksum, 0, ProtocolDefined.LENGTH_CHECKSUM);

        // 返回相应的数组
        return bb.array();
    }

    private byte getRemote(byte id, byte type) {
        return ByteUtil.mergeBytes(id, type);
    }

    private byte getLocal(byte id, byte type) {
        return ByteUtil.mergeBytes(id, type);
    }

    private int getTotalLen(IMessage message) {
        return ProtocolDefined.LENGTH_HEADER + ProtocolDefined.LENGTH_REMOTE + ProtocolDefined.LENGTH_LOCAL + ProtocolDefined.LENGTH_DATA_SIZE +
                ProtocolDefined.LENGTH_ACTION + ProtocolDefined.LENGTH_DEVICE + getDataLen(message.getData()) + ProtocolDefined.LENGTH_CHECKSUM;
    }

    private int getDataLen(byte[] data) {
        return data == null ? 0 : data.length;
    }

    public String getString(byte[] header, byte[] feature, byte[] length, byte[] content, byte[] checkDigit, byte[] end) {
        return "Header(2)=[ " + HexUtil.bytesToString(header)
                + "] Type(1)=[ " + HexUtil.bytesToString(feature)
                + "] Data Length(4)=[ " + HexUtil.bytesToString(length)
                + "] Data Context=[ " + new String(content)
                + "] CS(2)=[ " + HexUtil.bytesToString(checkDigit)
                + "] End(2)=[ " + HexUtil.bytesToString(end) + "] .";
    }
}
